/**
 * WTF Engine
 *
 * License... etc.
 **
 * GUI Window.
 * 
 * The WTF Engine GUI Window System/Manager. Windows are built
 * from Widgets, which build on top of Overlays.
 * 
 * \sa		WtfEngine::Overlay
 **
 * Authors: Sean Micklethwaite, Mac Duy Hai
 **/

#include "gui_window.h"
#include "gui_layout.h"

#define OVERLAY_NEXT	glTranslatef(0, 0, 0.00001)

using namespace WtfGui;
using namespace WtfEngine;


/**** Dock Layout Class ****/

//class DockLayout: public WidgetLayoutStackItem<InterpolatedWidgetVector, InterpolatedWidgetVector> {
	
//
// Window
//

Window::Window(const String& sTitle) 
	: WidgetContainer(sTitle), msTitle(sTitle), mbDocked(false), mbSizeX(false), mbSizeY(false) {
	// default background
	setBackground(tBackground::Get("v_gui_windowBg"));
	
	// create title bar
	mTitlebar = new Titlebar(sTitle);
	mTitlebar->Move(-10, -40);
	
	mCloseButton = new Widgets::Button(new ObjFunctor<Window>(this, &Window::OnClose),
							" x", tFont::Get("v_gui_font"));
	mCloseButton->Resize(28, 26);

	mDockButton = new Widgets::Button(new ObjFunctor<Window>(this, &Window::DockToggle),
							" _", tFont::Get("v_gui_font"));
	mDockButton->Resize(28, 26);
	
	// The order is important here - those added first are considered to be on top.
	AddChild(mDockButton);
	AddChild(mCloseButton);
	AddChild(mTitlebar);
	
	// Set padding to accommodate for titlebar
	setPadding(IWidgetLayout::tPadding(iVector2D(10, 40), iVector2D(10, 10)));
	
	// Set window to draggable
	setDragDropType(-1);
	
	// default size
	Resize(400, 400);
};

void Window::Dock() {
	WidgetContainer::tRef rDock = Window::getWindowDock();
	if(rDock.isNull()) {
		setLayout(new WidgetLayoutStackItem<>(getLayout()));
		Resize(getSize()[0], 30);
	} else {
		rDock->AddChild(this);
		Remove();
	};
};

void Window::Undock() {
	WidgetContainer::tRef rDock = Window::getWindowDock();
	if(rDock.isNull()) {
		WidgetLayoutStackItem<> * pLayout = dynamic_cast<WidgetLayoutStackItem<> *>(&*getLayout());
		assert(pLayout);
		setLayout(pLayout->getPreviousLayout());
	} else {
		rDock->AddChild(this);
		Remove();
	};
};

void Window::DockToggle() {
	if(mbDocked) {
		Undock();
	} else {
		Dock();
	};
	mbDocked = !mbDocked;
};

WidgetContainer::tRef Window::getWindowDock() const {
	IWindowContainer * pWindowCtr = dynamic_cast<IWindowContainer *>(this->mpParent);
	if(pWindowCtr) {
		return pWindowCtr->getWindowDock();
	} else {
		return WidgetContainer::tRef();
	};
};

void Window::OnResize() {
	mTitlebar->Resize(getSize()[0], 30);
	mTitlebar->Move(-getPadding()[0]);
	mCloseButton->Move(getSize()[0] - (getPadding()[0][0] + 32), 2 - getPadding()[0][1]);
	mDockButton->Move(getSize()[0] - (getPadding()[0][0] + 32 + 32 + 2), 2 - getPadding()[0][1]);
}

void Window::Paint(float d) {
	WidgetContainer::Paint(d);
}

bool Window::OnMouseDown(const iVector2D& vPos, tMouseButton btn){
	if(mpParent){
		mpParent->Select(this);
		mpParent->SendToFront(this);
	};
	
	// Check for resize
	iVector2D vSize = getSize();
	IWidgetLayout::tPadding vPadding = getPadding();
	if(vPos[0] > vSize[0] - vPadding[1][0])
		mbSizeX = true;
	if(vPos[1] > vSize[1] - vPadding[1][1])
		mbSizeY = true;
	
	bool bHandled = mbSizeX || mbSizeY || WidgetContainer::OnMouseDown(vPos, btn);
	return bHandled || true;
};

bool Window::OnMouseUp(const iVector2D& vPos, tMouseButton btn) {
	bool bHandled = mbSizeX || mbSizeY || WidgetContainer::OnMouseUp(vPos, btn);
	mbSizeX = mbSizeY = false;
	// Always handled
	return bHandled || true;
};

bool Window::OnMouseMove(const iVector2D &vPos, const iVector2D &vDelta) {
	iVector2D vSize = getSize();
	if(mbSizeX) {
		vSize[0] += vDelta[0];
	};
	if(mbSizeY) {
		vSize[1] += vDelta[1];
	};

	if(mbSizeX || mbSizeY) {
		// Resize without interpolation
		getLayout()->initSize(vSize);
		OnResize();
	} else {
		WidgetContainer::OnMouseMove(vPos, vDelta);
	};

	return true;
};

/// Closes the window
void Window::OnClose() {
	Remove();
};


/**** Screen Overlay ****/

ScreenOverlay::ScreenOverlay()
: WidgetContainer("Window Manager"), mScreenPos(Renderer::GetInstance().GetScreen()->getSize() / -2)
{
	Resize(Renderer::GetInstance().GetScreen()->getSize());
	setBackground(new tBackground("overlay", new NoBackground()));
};

void ScreenOverlay::Paint(float d) {
	// Translate to top-left
	glTranslatef(0, getRealSize()[1], 0);

	glPushAttrib(GL_STENCIL_TEST | GL_DEPTH_TEST);
	//glEnable(GL_STENCIL_TEST);
	glDisable(GL_DEPTH_TEST);

	WidgetContainer::Paint(d);
	
	glPopAttrib();
};

void ScreenOverlay::Init() {
	// Bind to input events
	GameState::tRef rState = Kernel::GetInstance().GetGameState();

	rState->BindEvent(new KeyEvent(),
		EventHandler(new ObjFunctor<ScreenOverlay>(this, &ScreenOverlay::KeyPress),
		EventHandler::PRIO_WM));
	rState->BindEvent(new MouseDownEvent(),
		EventHandler(new ObjFunctor<ScreenOverlay>(this, &ScreenOverlay::MouseDown),
		EventHandler::PRIO_WM));
	rState->BindEvent(new MouseUpEvent(),
		EventHandler(new ObjFunctor<ScreenOverlay>(this, &ScreenOverlay::MouseUp),
		EventHandler::PRIO_WM));
	rState->BindEvent(new MouseMoveEvent(),
		EventHandler(new ObjFunctor<ScreenOverlay>(this, &ScreenOverlay::MouseMove),
		EventHandler::PRIO_WM));


	// Add to renderer
	if(!Renderer::GetInstance().HasOverlay(this))
		Renderer::GetInstance().PushOverlay(this);
};

void ScreenOverlay::Remove() {
	// TODO: unbind from input events
	GameState::tRef rState = Kernel::GetInstance().GetGameState();

	// Remove from renderer
	Renderer::GetInstance().RemoveOverlay(this);
};

void ScreenOverlay::KeyPress() {
	const KeyEvent* pEv = Kernel::GetInstance().GetCurrentEvent<KeyEvent>();

	if(OnKeyPress(pEv->getKey().unicode, pEv->getKey().flags))
		Kernel::GetInstance().SetEventStatus(Event::STOPPED);
};

void ScreenOverlay::MouseDown() {
	const MouseDownEvent * pEv = Kernel::GetInstance().GetCurrentEvent<MouseDownEvent>();

	if(OnMouseDown(iVector2D(pEv->x(), pEv->y()), pEv->getButton()))
		Kernel::GetInstance().SetEventStatus(Event::STOPPED);
};

void ScreenOverlay::MouseUp() {
	const MouseUpEvent * pEv = Kernel::GetInstance().GetCurrentEvent<MouseUpEvent>();

	if(OnMouseUp(iVector2D(pEv->x(), pEv->y()), pEv->getButton()))
		Kernel::GetInstance().SetEventStatus(Event::STOPPED);
};

void ScreenOverlay::MouseMove() {
	const MouseMoveEvent * pEv = Kernel::GetInstance().GetCurrentEvent<MouseMoveEvent>();

	if(OnMouseMove(iVector2D(pEv->x(), pEv->y()), iVector2D(pEv->GetDeltaX(), pEv->GetDeltaY())))
		Kernel::GetInstance().SetEventStatus(Event::STOPPED);
};


/**** Window Manager ****/

WindowManager::WindowManager()
: ScreenOverlay(), isActive(false), mBgColour()
{
};

void WindowManager::Paint(float d) {
	iVector2D vSize = getRealSize();

	// Draw backdrop overlay
	
	glColor4fv(mBgColour.Value.toArray());

	glBegin(GL_QUADS);
		glVertex3f(0, 0, 0);
		glVertex3f(vSize[0], 0, 0);
		glVertex3f(vSize[0], vSize[1], 0);
		glVertex3f(0, vSize[1], 0);
	glEnd();

	ScreenOverlay::Paint(d);
};

void WindowManager::WmToggle() {
	if(isActive){
		Kernel::GetInstance().PopGameState();
	}else{
		Kernel::GetInstance().ForkGameState(new WmGameState(this));
	};

	if(Kernel::GetInstance().GetEventStatus() != Event::NONE) {
		Kernel::GetInstance().SetEventStatus(Event::STOPPED);
	};
};

void WindowManager::WmSetActive(bool b) {
	if(isActive == b)
		return;
	isActive = b;
	if(isActive){
		
		//
		// Add to overlay list, and fade in
		//
		
		mBgColour = DataManager::GetInstance().Load<Colour>("v_gui_wmBg")->getValue();
		mBgColour.rInterpolator->SetOnComplete(Functor::tRef());
		Init();
		
	} else {
		
		//
		// Fade out
		//
		
		mBgColour = fVector4D();
		mBgColour.rInterpolator->SetOnComplete(new ObjFunctor<WindowManager>(this, &WindowManager::WmClose));
	};
};

/**
 * Close event - triggered when fade out is finished.
 **/
void WindowManager::WmClose() {
	// Remove from overlay list
	Remove();
};

void WindowManager::BindToEvent(Event* pEv) {
	if(pEv)
		mrToggleEvent = pEv;

	if(!mrToggleEvent.isNull()){
		Kernel::GetInstance().GetGameState()->BindEvent(
			&*mrToggleEvent, Functor::tRef(new ObjFunctor<WindowManager>(GcReference<WindowManager>(this), &WindowManager::WmToggle)));
	};
};

bool WindowManager::OnKeyPress(int key, int flags){
	switch(key){
		case InputKey::ESC:
			return true;
		case '\t':
			if(flags & KMOD_CTRL){
				NextChild<Window>();
				if(!mrActiveChild.isNull())
					SendToFront(mrActiveChild);
				return true;
			};
			break;
		case 23: // Ctrl + w
			if(!mrActiveChild.isNull() && dynamic_cast<Window*>(&*mrActiveChild)){
				dynamic_cast<Window*>(&*mrActiveChild)->OnClose();
				return true;
			};
			break;
	};
	
	if(!mrActiveChild.isNull())
		return mrActiveChild->OnKeyPress(key, flags);
	
	return false;
};

void WmGameState::Enable() {
	// Clear input events - catch them
	ClearEvents(new MouseMoveEvent());
	ClearEvents(new MouseDownEvent());
	ClearEvents(new MouseUpEvent());
	ClearEvents(new KeyedEvent<KeyDownEvent>());
	ClearEvents(new KeyedEvent<KeyUpEvent>());
	ClearEvents(new KeyEvent());

	// Rebind
	mrWinMgr->BindToEvent();

	mrWinMgr->WmSetActive(true);
	
	InputHandler::GetInstance().EnterGuiMode();

	GameState::Enable();
};

void WmGameState::Disable() {
	mrWinMgr->WmSetActive(false);
	GameState::Disable();
};
